package com.cym.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.cym.model.Param;
import com.cym.model.Upstream;
import com.cym.model.UpstreamServer;
import com.cym.sqlhelper.bean.Page;
import com.cym.sqlhelper.bean.Sort;
import com.cym.sqlhelper.bean.Sort.Direction;
import com.cym.sqlhelper.bean.Update;
import com.cym.sqlhelper.utils.ConditionAndWrapper;
import com.cym.sqlhelper.utils.ConditionOrWrapper;
import com.cym.sqlhelper.utils.SqlHelper;
import com.cym.utils.NginxUtils;
import com.github.odiszapc.nginxparser.*;
import org.noear.solon.annotation.Inject;
import org.noear.solon.aspect.annotation.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 负载均衡服务
 */
@Service
public class UpstreamService {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Inject
    SqlHelper sqlHelper;

    public Page search(Page page, String word) {
        ConditionAndWrapper conditionAndWrapper = new ConditionAndWrapper();

        if (StrUtil.isNotEmpty(word)) {
            conditionAndWrapper.and(new ConditionOrWrapper().like("name", word));
        }

        page = sqlHelper.findPage(conditionAndWrapper, new Sort("seq", Direction.DESC), page, Upstream.class);

        return page;
    }


    public void deleteById(String id) {
        sqlHelper.deleteById(id, Upstream.class);
        sqlHelper.deleteByQuery(new ConditionAndWrapper().eq("upstreamId", id), UpstreamServer.class);
    }


    public void addOver(Upstream upstream, List<UpstreamServer> upstreamServers, String upstreamParamJson) {
        if (upstream.getProxyType() == 1 || upstream.getTactics() == null) {
            upstream.setTactics("");
        }

        sqlHelper.insertOrUpdate(upstream);

        List<Param> paramList = new ArrayList<Param>();
        if (StrUtil.isNotEmpty(upstreamParamJson) && JSONUtil.isTypeJSON(upstreamParamJson)) {
            paramList = JSONUtil.toList(JSONUtil.parseArray(upstreamParamJson), Param.class);
        }
        sqlHelper.deleteByQuery(new ConditionAndWrapper().eq("upstreamId", upstream.getId()), Param.class);
        // 反向插入,保证列表与输入框对应
        Collections.reverse(paramList);
        for (Param param : paramList) {
            param.setUpstreamId(upstream.getId());
            sqlHelper.insert(param);
        }

        sqlHelper.deleteByQuery(new ConditionAndWrapper().eq("upstreamId", upstream.getId()), UpstreamServer.class);
        if (upstreamServers != null) {
            // 反向插入,保证列表与输入框对应
            Collections.reverse(upstreamServers);

            for (UpstreamServer upstreamServer : upstreamServers) {
                upstreamServer.setUpstreamId(upstream.getId());
                sqlHelper.insert(upstreamServer);
            }
        }

    }

    public List<UpstreamServer> getUpstreamServers(String id) {
        return sqlHelper.findListByQuery(new ConditionAndWrapper().eq("upstreamId", id), UpstreamServer.class);
    }


//	public void del(String id) {
//		sqlHelper.deleteById(id, Upstream.class);
//		sqlHelper.deleteByQuery(new ConditionAndWrapper().eq("upstreamId", id), UpstreamServer.class);
//
//	}

    public List<Upstream> getListByProxyType(Integer proxyType) {
        Sort sort = new Sort().add("seq", Direction.DESC);
        return sqlHelper.findListByQuery(new ConditionAndWrapper().eq("proxyType", proxyType), sort, Upstream.class);
    }

    public Long getCountByName(String name) {
        return sqlHelper.findCountByQuery(new ConditionAndWrapper().eq("name", name), Upstream.class);
    }

    public Long getCountByNameWithOutId(String name, String id) {
        return sqlHelper.findCountByQuery(new ConditionAndWrapper().eq("name", name).ne("id", id), Upstream.class);
    }

    public List<UpstreamServer> getServerListByMonitor(int monitor) {
        List<String> upstreamIds = sqlHelper.findIdsByQuery(new ConditionAndWrapper().eq("monitor", monitor), Upstream.class);

        return sqlHelper.findListByQuery(new ConditionAndWrapper().in("upstreamId", upstreamIds), UpstreamServer.class);
    }

    public List<UpstreamServer> getAllServer() {
        return sqlHelper.findAll(UpstreamServer.class);
    }

    public void resetMonitorStatus() {

        sqlHelper.updateMulti(new ConditionAndWrapper(), new Update().set("monitorStatus", -1), UpstreamServer.class);
    }


    public void setSeq(String upstreamId, Integer seqAdd) {
        Upstream upstream = sqlHelper.findById(upstreamId, Upstream.class);

        List<Upstream> upstreamList = sqlHelper.findAll(new Sort("seq", Direction.DESC), Upstream.class);
        if (upstreamList.size() > 0) {
            Upstream tagert = null;
            if (seqAdd < 0) {
                // 下移
                for (int i = 0; i < upstreamList.size(); i++) {
                    if (upstreamList.get(i).getSeq() < upstream.getSeq()) {
                        tagert = upstreamList.get(i);
                        break;
                    }
                }
            } else {
                // 上移
                for (int i = upstreamList.size() - 1; i >= 0; i--) {
                    if (upstreamList.get(i).getSeq() > upstream.getSeq()) {
                        tagert = upstreamList.get(i);
                        break;
                    }
                }
            }

            if (tagert != null) {
                // 交换seq
                Long seq = tagert.getSeq();
                tagert.setSeq(upstream.getSeq());
                upstream.setSeq(seq);

                sqlHelper.updateById(tagert);
                sqlHelper.updateById(upstream);
            }

        }

    }

    /**
     * 导入负载均衡配置
     */
    public void importUpstream(String nginxPath) throws Exception {
        String initNginxPath = NginxUtils.initNginx(nginxPath);
        NgxConfig conf = NginxUtils.readByNginxPath(initNginxPath);

        // 获取负载均衡配置
        NgxBlock http = conf.findBlock("http");
        if (http != null) {
            List<NgxEntry> upstreams = http.findAll(NgxConfig.BLOCK, "upstream");
            upstreams.forEach(up -> {
                NgxBlock upp = (NgxBlock) up;
                logger.debug("upstream {} : {}", upp.getName(), upp.getValue());
                Upstream upstream = new Upstream();
                upstream.setName(upp.getValue());
                sqlHelper.insert(upstream);

                List<UpstreamServer> upstreamServers = new ArrayList<>();
                upp.getEntries().forEach(e -> {
                    NgxParam ns = (NgxParam) e;
                    UpstreamServer us = new UpstreamServer();
                    us.setUpstreamId(upstream.getId());
                    String serverStr = ns.getTokens()
                            .stream()
                            .map(NgxToken::getToken)
                            .collect(Collectors.joining(" "));
                    logger.debug("提取的负载均衡server: {}", serverStr);
                    // 提取IP和端口
                    String[] serverPort = NginxUtils.UpStream.regServerPort(serverStr);
                    us.setServer(serverPort[0]);
                    us.setPort(Integer.parseInt(serverPort[1]));
                    // 提取最大失败次数
                    us.setMaxFails(NginxUtils.UpStream.regMaxFails(serverStr, 3));
                    // 提取失败超时周期
                    us.setFailTimeout(NginxUtils.UpStream.regFailTimeout(serverStr, 10));
                    // 提取权重
                    us.setWeight(NginxUtils.UpStream.regWeight(serverStr, 1));

                    logger.debug("server: {}", JSONUtil.toJsonStr(us));
                    upstreamServers.add(us);
                });
                sqlHelper.insertAll(upstreamServers);
            });
        }
        // 删除临时文件
        FileUtil.del(initNginxPath);
    }

}
